﻿using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Input;
using CardPlay.Client.ViewModels;
using CardPlay.Model.GameModel;

namespace CardPlay.Client.Tools
{
    public abstract class DragDropTool : Tool
    {
        private Point _dragPosition;
        private Rect _dragDetect;
        private IDraggable _dragItem;

        protected DragDropTool(IToolSite host) : base(host)
        {
            State = Inactive;
        }

        private IPlayingContext PlayingContext
        {
            get { return (IPlayingContext)Site.GetService(typeof(IPlayingContext)); }
        }

        private void Inactive(InputEvent inputEvent)
        {
            if (inputEvent.Name != InputEvent.MouseLeftButtonDown) return;

            CaptureElement = inputEvent.InputElement;

            //var uiElement = (ContentElement) inputEvent.InputElement;
            //uiElement.Focusable = true;
            //uiElement.Focus();

            Debug.WriteLine("Drag: detect");

            var dragSize = DragSize;

            _dragPosition = ((MouseEventArgs) inputEvent.Args).GetPosition(inputEvent.InputElement);
            _dragDetect = new Rect(_dragPosition.X - dragSize.Width, _dragPosition.Y - dragSize.Height, dragSize.Width, dragSize.Height);

            var args = (MouseButtonEventArgs)inputEvent.Args;
            var position = args.GetPosition(CaptureElement);

            var location = PlayingContext.Canvas.HitTest((int)position.X, (int)position.Y);
            if (location.IsUndefined) return;
            
            _dragItem = CreateDragObject(location);

            IsActivated = true;
            State = DetectingDragStart;
        }

        private void DetectingDragStart(InputEvent inputEvent)
        {
            if (inputEvent.Name == InputEvent.MouseLeftButtonUp || IsCancelledBy(inputEvent))
            {
                OnCancelDrag();
            }
            else if (_dragItem != null && inputEvent.Name == InputEvent.MouseMove)
            {
                var position = ((MouseEventArgs)inputEvent.Args).GetPosition(inputEvent.InputElement);

                if (! _dragDetect.Contains(position))
                {
                    OnMouseMoved(position);
                    State = Dragging;
                }
            }
        }

        private void Dragging(InputEvent inputEvent)
        {
            if (_dragItem == null || IsCancelledBy(inputEvent))
            {
                OnCancelDrag();
            }
            else switch (inputEvent.Name)
            {
                case InputEvent.MouseMove:
                    OnMouseMoved(((MouseEventArgs)inputEvent.Args).GetPosition(inputEvent.InputElement));
                    break;
                case InputEvent.MouseLeftButtonUp:
                    OnCompleteDrag();
                    break;
            }
        }

        protected abstract IDraggable CreateDragObject(PartLocation location);

        protected override void OnDeactivated()
        {
            if (_dragItem != null)
            {
                _dragItem.Dispose();
                _dragItem = null;
            }

            CaptureElement = null;
            //Mouse.Capture(null);

            State = Inactive;

            Debug.WriteLine("Drag: ended");
        }

        private void OnCancelDrag()
        {
            IsActivated = false;
        }

        private void OnCompleteDrag()
        {
            if (_dragItem != null) _dragItem.OnDrop();

            IsActivated = false;
        }

        private void OnMouseMoved(Point position)
        {
            //Debug.WriteLine("Drag: offset " + (position - _dragPosition));

            _dragItem.OnDrag(new CardPlay.Model.Primitives.Size((int) (position.X - _dragPosition.X), (int)(position.Y - _dragPosition.Y)));
            _dragPosition = position;
        }

#if SILVERLIGHT
        private static Size DragSize
        {
            get { return new Size(3, 3); }
        }

#else

        [DllImport("user32.dll")]
        private static extern int GetSystemMetrics(SystemMetric smIndex);

        // ReSharper disable InconsistentNaming
        private enum SystemMetric
        {
            SM_CXDRAG = 68,
            SM_CYDRAG = 69
        }
        // ReSharper restore InconsistentNaming

        private static Size DragSize
        {
            get
            {
                return new Size(GetSystemMetrics(SystemMetric.SM_CXDRAG), GetSystemMetrics(SystemMetric.SM_CYDRAG));
            }
        }
#endif
    }
}